package com.wisdge.web.filters;

import java.io.IOException;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;

/**
 * @projectName: ngsapitask-ms
 * @package: com.wisdge.web.filters
 * @className: XSSPreventionFilter
 * @author: LiJT
 * @description: TODO
 * @date: 2024-01-11 23:32
 * @version: 1.0
 */

public class XSSPreventionFilter implements Filter {
    private static final Log log = LogFactory.getLog(XSSPreventionFilter.class);
    private static final String[] filterLabels = new String[]{"applet", "blink", "frameset", "iframe", "object", "base", "body", "head", "layer", "style", "basefont", "embed", "html", "link", "title", "bgsound", "frame", "ilayer", "meta", "script"};
    private static final String[] filterAttributes = new String[]{"dynsrc", "action", "background", "bgsound", "lowsrc", "value"};
    private static final String[] filterKeywords = new String[]{"vbscript:", "ms-its:", "firefoxurl:", "javascript:", "mhtml:", "mocha:", "data:", "livescript:"};
    private FilterConfig filterConfig;

    public XSSPreventionFilter() {
    }

    public void destroy() {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String allowDomains = this.filterConfig.getInitParameter("allowDomains");
        log.debug("allowDomains:" + allowDomains);
        String allowPaths = this.filterConfig.getInitParameter("allowPaths");
        log.debug("allowPaths:" + allowPaths);
        String servletPath = ((HttpServletRequest) request).getPathInfo();
        if (servletPath == null) {
            servletPath = ((HttpServletRequest) request).getServletPath();
        }
        log.debug("servletPath:" + servletPath);

        if (!StringUtils.isEmpty(allowPaths)) {
            String[] var7 = allowPaths.split(",");
            int var8 = var7.length;

            for (int var9 = 0; var9 < var8; ++var9) {
                String allow = var7[var9];
                if (servletPath.startsWith(allow)) {
                    chain.doFilter(request, response);
                    return;
                }
            }
        }

        XSSRequestWrapper wrapper = new XSSRequestWrapper((HttpServletRequest) request, allowDomains);
        Map<String, String[]> parameterMap = wrapper.getParameterMap();
        log.debug("parameterMap:" + parameterMap.toString());
        // parameterMap =cleanXSSForNormalRequest(parameterMap);
        // wrapperRequest.setParameterMap(parameterMap);


        chain.doFilter(wrapper, response);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    public static String filter(String value) {
        String cleanValue = Normalizer.normalize(value, Form.NFD);
        cleanValue = cleanValue.replaceAll("\u0000", "").replaceAll("'", "");
        Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", 2);
        cleanValue = scriptPattern.matcher(cleanValue).replaceAll("");
        scriptPattern = Pattern.compile("eval\\((.*?)\\)", 42);
        cleanValue = scriptPattern.matcher(cleanValue).replaceAll(" ");
        scriptPattern = Pattern.compile("expression\\((.*?)\\)", 42);
        cleanValue = scriptPattern.matcher(cleanValue).replaceAll(" ");
        scriptPattern = Pattern.compile("onload(.*?)=", 42);
        cleanValue = scriptPattern.matcher(cleanValue).replaceAll(" ");
        scriptPattern = Pattern.compile("alert\\((.*?)\\)", 42);
        cleanValue = scriptPattern.matcher(cleanValue).replaceAll("");
        String[] var3 = filterAttributes;
        int var4 = var3.length;

        int var5;
        String filterLabel;
        for (var5 = 0; var5 < var4; ++var5) {
            filterLabel = var3[var5];
            scriptPattern = Pattern.compile(filterLabel + "[\r\n]*=[\r\n]*(\\\"|')(.*?)(\\\"|')", 42);
            cleanValue = scriptPattern.matcher(cleanValue).replaceAll(" ");
        }

        var3 = filterKeywords;
        var4 = var3.length;

        for (var5 = 0; var5 < var4; ++var5) {
            filterLabel = var3[var5];
            scriptPattern = Pattern.compile(filterLabel, 2);
            cleanValue = scriptPattern.matcher(cleanValue).replaceAll(" ");
        }

        var3 = filterLabels;
        var4 = var3.length;

        for (var5 = 0; var5 < var4; ++var5) {
            filterLabel = var3[var5];
            scriptPattern = Pattern.compile("<" + filterLabel + "(.*?)>", 42);
            cleanValue = scriptPattern.matcher(cleanValue).replaceAll(" ");
            scriptPattern = Pattern.compile("</" + filterLabel + ">", 2);
            cleanValue = scriptPattern.matcher(cleanValue).replaceAll(" ");
        }

        cleanValue = cleanEventAttact(cleanValue);
        cleanValue = cleanValue.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
        return cleanValue;
    }

    public static String cleanEventAttact(String value) {
        Pattern compile = Pattern.compile("onafterprint(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onbeforeprint(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onbeforeunload(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onerror(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onhaschange(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onload(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onmessage(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onoffline(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("ononline(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onpagehide(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onpageshow(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onpopstate(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onredo(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onresize(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onstorage(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onundo(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onunload(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onblur(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onchange(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("oncontextmenu(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onfocus(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onformchange(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onforminput(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("oninput(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("oninvalid(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onreset(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onselect(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onsubmit(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onkeydown(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onkeypress(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onkeyup(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onclick(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("ondblclick(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("ondrag(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("ondragend(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("ondragenter(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("ondragleave(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("ondragover(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("ondragstart(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("ondrop(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onmousedown(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onmousemove(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onmouseout(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onmouseover(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onmouseenter(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onmouseup(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onmousewheel(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        compile = Pattern.compile("onscroll(.*?)=", 42);
        value = compile.matcher(value).replaceAll("");
        value = value.replace("document", "");
        value = value.replace("alert", "");
        return value;
    }

    class XSSRequestWrapper extends HttpServletRequestWrapper {
        private String allowDomains;
        private Map<String, String[]> sanitizedQueryString;

        public XSSRequestWrapper(HttpServletRequest request, String allowDomains) {
            super(request);
            this.allowDomains = allowDomains;
            log.debug("allowDomains:" + allowDomains);
        }

        public String getParameter(String name) {
            String parameter = null;
            String[] vals = (String[]) this.getParameterMap().get(name);
            log.debug("getParameter String[] vals:" + vals.toString());
            if (vals != null && vals.length > 0) {
                parameter = vals[0];
            }

            return parameter;
        }

        public String[] getParameterValues(String name) {
            return (String[]) this.getParameterMap().get(name);
        }

        public Enumeration<String> getParameterNames() {
            return Collections.enumeration(this.getParameterMap().keySet());
        }

        public Map<String, String[]> getParameterMap() {
            if (this.sanitizedQueryString == null) {
                Map<String, String[]> res = new HashMap();
                Map<String, String[]> originalQueryString = super.getParameterMap();
                if (originalQueryString != null) {
                    Iterator var3 = originalQueryString.keySet().iterator();

                    while (var3.hasNext()) {
                        String key = (String) var3.next();
                        String[] rawVals = (String[]) originalQueryString.get(key);
                        String[] snzVals = new String[rawVals.length];

                        for (int i = 0; i < rawVals.length; ++i) {
                            snzVals[i] = this.stripXSS(rawVals[i]);
                        }

                        res.put(this.stripXSS(key), snzVals);
                    }
                }

                this.sanitizedQueryString = res;
            }

            return this.sanitizedQueryString;
        }

        private String stripXSS(String value) {
            String cleanValue = null;
            if (value != null) {
                cleanValue = XSSPreventionFilter.filter(value);
                if (this.allowDomains != null && !this.allowDomains.equals("")) {
                    String[] allDomainsArr = this.allowDomains.toLowerCase().split(",");
                    Pattern scriptPattern = Pattern.compile("src[\r\n\\s]*=[\r\n\\s]*(\\\"|'|\\\\\")(.*?)(\\\"|'|\\\\\")", 42);
                    Matcher srcMatcher = scriptPattern.matcher(cleanValue);

                    while (true) {
                        String gp;
                        boolean findAllowDomain;
                        do {
                            int gc;
                            do {
                                if (!srcMatcher.find()) {
                                    return cleanValue;
                                }

                                gc = srcMatcher.groupCount();
                            } while (gc < 1);

                            gp = srcMatcher.group(2);
                            findAllowDomain = false;
                        } while (gp == null);

                        gp = gp.toLowerCase();
                        if (!gp.startsWith("http://") && !gp.startsWith("https://")) {
                            findAllowDomain = true;
                        } else {
                            String[] var9 = allDomainsArr;
                            int var10 = allDomainsArr.length;

                            for (int var11 = 0; var11 < var10; ++var11) {
                                String allowDomain = var9[var11];
                                if (gp.startsWith(allowDomain)) {
                                    findAllowDomain = true;
                                    break;
                                }
                            }
                        }

                        if (!findAllowDomain) {
                            scriptPattern = Pattern.compile("src[\r\n\\s]*=[\r\n\\s]*(\\\"|'|\\\\\")" + gp + "(\\\"|'|\\\\\")", 42);
                            cleanValue = scriptPattern.matcher(cleanValue).replaceAll("");
                        }
                    }
                }
            }

            return cleanValue;
        }
    }
}
